﻿/**
 * Feature： 配置文件管理类
 *           获取和保存配置值
 * Author: Shizhy
 * Date: 2016-01-18
 */

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Xml;

namespace DotNet.Configure
{
    public class ConfigureManager
    {
        readonly Hashtable _configurationsCache = Hashtable.Synchronized(new Hashtable());
        private string _configFilePath = System.AppDomain.CurrentDomain.SetupInformation.ApplicationBase + "\\configurations.dat";
        /// <summary>
        /// 配置文件路径
        /// </summary>
        public string ConfigPath
        {
            get { return _configFilePath; }
            set
            {
                _configFilePath = value;
                if (!File.Exists(_configFilePath))
                {
                    File.WriteAllText(_configFilePath,
                        "<?xml version=\"1.0\" encoding=\"utf-8\" ?>"
                        + Environment.NewLine + "<configurations>"
                        + Environment.NewLine + "</configurations>");
                }
            }
        }

        private static ConfigureManager _manager;
        public static ConfigureManager Instance
        {
            get { return _manager ?? (_manager = new ConfigureManager()); }
        }
        private ConfigureManager()
        {
            ConfigPath = _configFilePath;
            ReLoad();
        }
        /// <summary>
        /// 获取指定索引名称的数据值
        /// </summary>
        /// <param name="name">索引名称</param>
        public ConfigValue Get(string name)
        {
            if (string.IsNullOrEmpty(name))
            {
                return null;
            }
            ConfigValue cfgvalue = new ConfigValue();
            var cfg = (ConfigValue) _configurationsCache[name];
            if (cfg != null)
            {
                cfgvalue.Name = cfg.Name;
                cfgvalue.DataType = cfg.DataType;
                cfgvalue.Value = cfg.Value;
                cfgvalue.NodeType = cfg.NodeType;
            }
            return cfgvalue;
        }

        /// <summary>
        /// 获取配置列表
        /// </summary>
        /// <returns></returns>
        public List<ConfigValue> GetList()
        {
            List<ConfigValue> templs = new List<ConfigValue>();
            foreach (object o in _configurationsCache.Values)
            {
                templs.Add((ConfigValue) o);
            }
            templs.Sort((o1,o2)=> String.Compare(o1.Name, o2.Name, StringComparison.InvariantCultureIgnoreCase));
            return templs;
        }

        /// <summary>
        /// 设置数据的到配置文件
        /// </summary>
        /// <param name="configuration">配置数据</param>
        public bool Set(ConfigValue configuration)
        {
            if (configuration==null || string.IsNullOrEmpty(configuration.Name))
            {
                Console.WriteLine("Null configuration value");
                return false;
            }
            try
            {
                //修改缓存的内容
                _configurationsCache[configuration.Name] = configuration;

                //设置配置文件内容
                XmlDocument dom = new XmlDocument();
                dom.Load(_configFilePath);
                if (dom.DocumentElement != null)
                {
                    bool haved = false;
                    foreach (var node in dom.DocumentElement.ChildNodes)
                    {
                        XmlElement cfgEle = node as XmlElement;
                        if (cfgEle != null && cfgEle.GetAttribute("name") == configuration.Name)
                        {
                            cfgEle.RemoveAll();
                            cfgEle.SetAttribute("name", configuration.Name);
                            cfgEle.SetAttribute("datatype", configuration.DataType.FullName);
                            cfgEle.SetAttribute("nodetype", configuration.NodeType.ToString());
                            if (configuration.NodeType == NodeType.Array)
                            {
                                var trans = (Array) configuration.Value;
                                foreach (var tran in trans)
                                {
                                    XmlElement ele = dom.CreateElement("value");
                                    ele.InnerText = tran.ToString();
                                    cfgEle.AppendChild(ele);
                                }
                            }
                            else
                            {
                                XmlElement ele = dom.CreateElement("value");
                                ele.InnerText = configuration.Value.ToString();
                                cfgEle.AppendChild(ele);
                            }
                            haved = true;
                            break;
                        }
                    }
                    if (!haved)
                    {
                        //没有找到就创建一个
                        XmlElement cfgEle = dom.CreateElement("element");
                        cfgEle.SetAttribute("name", configuration.Name);
                        cfgEle.SetAttribute("datatype", configuration.DataType.FullName);
                        cfgEle.SetAttribute("nodetype", configuration.NodeType.ToString());

                        //NodeType的价值在这里就体现出来了
                        if (configuration.NodeType == NodeType.Array)
                        {
                            var trans = (Array) configuration.Value;
                            foreach (var tran in trans)
                            {
                                XmlElement ele = dom.CreateElement("value");
                                ele.InnerText = tran.ToString();
                                cfgEle.AppendChild(ele);
                            }
                        }
                        else
                        {
                            XmlElement ele = dom.CreateElement("value");
                            ele.InnerText = configuration.Value.ToString();
                            cfgEle.AppendChild(ele);
                        }

                        if (dom.DocumentElement != null)
                        {
                            dom.DocumentElement.AppendChild(cfgEle);
                        }
                        else
                        {
                            XmlElement root = dom.CreateElement("configurations");
                            dom.AppendChild(root);
                        }
                    }
                    dom.Save(_configFilePath);
                }
                return true;
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                return false;
            }
        }

        /// <summary>
        /// 重新加载数据信息
        /// </summary>
        public bool ReLoad()
        {
            //装载配置文件
            _configurationsCache.Clear();
            XmlDocument dom = new XmlDocument();
            try
            {
                dom.Load(_configFilePath);
                if (dom.DocumentElement != null)
                {
                    foreach (var node in dom.DocumentElement.ChildNodes)
                    {
                        XmlElement cfgEle = node as XmlElement;
                        if (cfgEle != null)
                        {
                            ConfigValue cfg = new ConfigValue();
                            cfg.Name = cfgEle.GetAttribute("name");
                            cfg.DataType = Type.GetType(cfgEle.GetAttribute("datatype"));
                            cfg.NodeType = (NodeType)Enum.Parse(typeof(NodeType), cfgEle.GetAttribute("nodetype"));

                            if (cfg.NodeType == NodeType.Array)
                            {
                                Type dataType = cfg.DataType;

                                int len = cfgEle.ChildNodes.Count;
                                if (dataType != null)
                                {
                                    var trans = Array.CreateInstance(dataType, len);

                                    for (int i = 0; i < len; i++)
                                    {
                                        var tValue = ConvertObject(cfgEle.ChildNodes[i].InnerText, dataType);
                                        trans.SetValue(tValue, i);
                                    }
                                    cfg.Value = trans;
                                }
                            }
                            else
                            {
                                var tValue = ConvertObject(cfgEle.FirstChild.InnerText, cfg.DataType);
                                cfg.Value = tValue;
                            }
                            _configurationsCache.Add(cfg.Name, cfg);
                        }
                    }
                }
                return true;
            }
            catch (Exception ex)
            {
                Console.WriteLine("读数配置文件遇到故障:{0}", ex.Message);
                return false;
            }
        }

        /// <summary>
        /// 将一个对象转换为指定类型
        /// </summary>
        /// <param name="obj">待转换的对象</param>
        /// <param name="type">目标类型</param>
        /// <returns>转换后的对象</returns>
        public object ConvertObject(object obj, Type type)
        {
            if (type == null) return obj;
            if (obj == null) return type.IsValueType ? Activator.CreateInstance(type) : null;

            Type underlyingType = Nullable.GetUnderlyingType(type);
            if (type.IsInstanceOfType(obj)) // 如果待转换对象的类型与目标类型兼容，则无需转换
            {
                return obj;
            }

            // 如果待转换的对象的基类型为枚举
            if ((underlyingType ?? type).IsEnum)
            {
                if (underlyingType != null && string.IsNullOrEmpty(obj.ToString())) // 如果目标类型为可空枚举，并且待转换对象为null 则直接返回null值
                {
                    return null;
                }
                return Enum.Parse(underlyingType ?? type, obj.ToString());
            }
            
            //如果目标类型的基类型实现了IConvertible，则直接转换
            if (typeof(IConvertible).IsAssignableFrom(underlyingType ?? type))
            {
                try
                {
                    return Convert.ChangeType(obj, underlyingType ?? type, null);
                }
                catch
                {
                    return underlyingType == null ? Activator.CreateInstance(type) : null;
                }
            }
            //转换类
            ConstructorInfo constructor = type.GetConstructor(Type.EmptyTypes);
            if (constructor != null)
            {
                object o = constructor.Invoke(null);
                PropertyInfo[] propertys = type.GetProperties();
                Type oldType = obj.GetType();
                foreach (PropertyInfo property in propertys)
                {
                    PropertyInfo p = oldType.GetProperty(property.Name);
                    if (property.CanWrite && p != null && p.CanRead)
                    {
                        property.SetValue(o, ConvertObject(p.GetValue(obj, null), property.PropertyType), null);
                    }
                }
                return o;
            }
            return obj;
        }
    }
}
